// jQuery.event.swipe
// 0.5
// Stephen Band

// Dependencies
// jQuery.event.move 1.2

// One of swipeleft, swiperight, swipeup or swipedown is triggered on
// moveend, when the move has covered a threshold ratio of the dimension
// of the target node, or has gone really fast. Threshold and velocity
// sensitivity changed with:
//
// jQuery.event.special.swipe.settings.threshold
// jQuery.event.special.swipe.settings.sensitivity

(function (thisModule) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], thisModule);
    } else if ((typeof module !== "undefined" && module !== null) && module.exports) {
        module.exports = thisModule;
    } else {
        // Browser globals
        thisModule(jQuery);
    }
})(function (jQuery, undefined) {
    var add = jQuery.event.add,

        remove = jQuery.event.remove,

    // Just sugar, so we can have arguments in the same order as
    // add and remove.
        trigger = function (node, type, data) {
            jQuery.event.trigger(type, data, node);
        },

        settings = {
            // Ratio of distance over target finger must travel to be
            // considered a swipe.
            threshold: 0.4,
            // Faster fingers can travel shorter distances to be considered
            // swipes. 'sensitivity' controls how much. Bigger is shorter.
            sensitivity: 6
        };

    function moveend(e) {
        var w, h, event;

        w = e.currentTarget.offsetWidth;
        h = e.currentTarget.offsetHeight;

        // Copy over some useful properties from the move event
        event = {
            distX: e.distX,
            distY: e.distY,
            velocityX: e.velocityX,
            velocityY: e.velocityY,
            finger: e.finger
        };

        // Find out which of the four directions was swiped
        if (e.distX > e.distY) {
            if (e.distX > -e.distY) {
                if (e.distX / w > settings.threshold || e.velocityX * e.distX / w * settings.sensitivity > 1) {
                    event.type = 'swiperight';
                    trigger(e.currentTarget, event);
                }
            }
            else {
                if (-e.distY / h > settings.threshold || e.velocityY * e.distY / w * settings.sensitivity > 1) {
                    event.type = 'swipeup';
                    trigger(e.currentTarget, event);
                }
            }
        }
        else {
            if (e.distX > -e.distY) {
                if (e.distY / h > settings.threshold || e.velocityY * e.distY / w * settings.sensitivity > 1) {
                    event.type = 'swipedown';
                    trigger(e.currentTarget, event);
                }
            }
            else {
                if (-e.distX / w > settings.threshold || e.velocityX * e.distX / w * settings.sensitivity > 1) {
                    event.type = 'swipeleft';
                    trigger(e.currentTarget, event);
                }
            }
        }
    }

    function getData(node) {
        var data = jQuery.data(node, 'event_swipe');

        if (!data) {
            data = {count: 0};
            jQuery.data(node, 'event_swipe', data);
        }

        return data;
    }

    jQuery.event.special.swipe =
        jQuery.event.special.swipeleft =
            jQuery.event.special.swiperight =
                jQuery.event.special.swipeup =
                    jQuery.event.special.swipedown = {
                        setup: function (data, namespaces, eventHandle) {
                            var data = getData(this);

                            // If another swipe event is already setup, don't setup again.
                            if (data.count++ > 0) {
                                return;
                            }

                            add(this, 'moveend', moveend);

                            return true;
                        },

                        teardown: function () {
                            var data = getData(this);

                            // If another swipe event is still setup, don't teardown.
                            if (--data.count > 0) {
                                return;
                            }

                            remove(this, 'moveend', moveend);

                            return true;
                        },

                        settings: settings
                    };
});
